﻿using System.Linq;
using StreetPacMan.Server.Facade;

namespace StreetPacMan.Server.Rules.PlayerLocationChangeRules
{
    public class UpdateLocationRule : IHandlePlayerLocationChangedRule
    {
        private readonly ApplesEatingRule ApplesRule;

        public UpdateLocationRule()
        {
            ApplesRule = new ApplesEatingRule();
        }
        public void Handle(PacManDataContext dataContext, ServerPollParameters serverPollParameters, ref bool shouldContinueHandling)
        {
            var updateEntity = dataContext.Players.Where(x => x.Id == serverPollParameters.EntityId).Single();

            //probably first step on the board
            if(updateEntity.LastGeoLat==0 && updateEntity.LastGeoLon==0)
            {
                UpdateLocation(updateEntity, serverPollParameters);
                return;
            }

            //if reasonable distance from last location - update
            var distanceFromLastValidLocation = TwoGeos.GetDistance(updateEntity.LastGeoLat, updateEntity.LastGeoLon, serverPollParameters.GeoLat, serverPollParameters.GeoLng);
            if (distanceFromLastValidLocation<75)
            {
                UpdateLocation(updateEntity, serverPollParameters);                
                return;
            }

            //if no previously suspicious positions (over 75 meters) - set suspect and return
            if(updateEntity.LastSuspectGeoLat.HasValue==false || 
               updateEntity.LastSuspectGeoLon.HasValue==false)
            {
                updateEntity.LastSuspectGeoLat = serverPollParameters.GeoLat;
                updateEntity.LastSuspectGeoLon= serverPollParameters.GeoLng;
                updateEntity.NumberOfConsecutiveSuspiciousLocations = 1;
                shouldContinueHandling = false;
                return;
            }

            //if position is same as last suspicious position - return
            var isSameLocationAsLastSuspicousLocation = updateEntity.LastSuspectGeoLat.Value == serverPollParameters.GeoLat &&
                                                        updateEntity.LastSuspectGeoLon.Value == serverPollParameters.GeoLng;
            if (isSameLocationAsLastSuspicousLocation)
            {
                updateEntity.NumberOfConsecutiveSuspiciousLocations ++;
                shouldContinueHandling = false;
                return;                
            }
            
            //if new location is less than 75 meters from last usspicious location - than the player continues to walk after glitch
            var distanceFromLastSuspiciousLocation = TwoGeos.GetDistance(updateEntity.LastSuspectGeoLat.Value, updateEntity.LastSuspectGeoLon.Value, serverPollParameters.GeoLat, serverPollParameters.GeoLng);
            if (distanceFromLastSuspiciousLocation < 75)
            {
                //glitch correction by eating all the apples in the middle of the two points
                updateEntity.Game.AddGameEvent(EventType.PlayerLocationCorrected, string.Format("{0} location corrected ",updateEntity.Name ), updateEntity.Id);
                PretendPlayerWalkedInPath(updateEntity, updateEntity.LastGeoLat, updateEntity.LastGeoLon,
                                          updateEntity.LastSuspectGeoLat.Value, updateEntity.LastSuspectGeoLon.Value);
                UpdateLocation(updateEntity, serverPollParameters);
                return;
            }

            //otherwise - update last suspicious location and return.
            updateEntity.LastSuspectGeoLat = serverPollParameters.GeoLat;
            updateEntity.LastSuspectGeoLon = serverPollParameters.GeoLng;
            updateEntity.NumberOfConsecutiveSuspiciousLocations++;
            shouldContinueHandling = false;
        }

        private void PretendPlayerWalkedInPath(Player player, double prevLat, double prevLon, double newLat, double newLon)
        {
            if (player.RoleEnum == PlayerRole.PacMan)
            {
                ApplesRule.EatAllApplesInPath(player,
                                              new TwoGeos(new Geo(prevLat, prevLon), new Geo(newLat, newLon)));
            }
        }

        private void UpdateLocation(Player updateEntity, ServerPollParameters serverPollParameters)
        {
            updateEntity.LastGeoLat = serverPollParameters.GeoLat;
            updateEntity.LastGeoLon = serverPollParameters.GeoLng;
            updateEntity.LastSuspectGeoLat = null;
            updateEntity.LastSuspectGeoLon = null;
            updateEntity.NumberOfConsecutiveSuspiciousLocations = 0;
        }
    }
}